#include "stdlib.hpp"
#include <purewell/pairsocket.hpp>

namespace purewell
{

#define _get_fd(idx) (idx == 0?m_fd:m_fd2)

#ifndef WIN32
#	define __PWX(x)		(x)
#	define __PWXC(x)	(x)
#else
#	define __PWX(x)		((char*)(x))
#	define __PWXC(x)	((const char*)(x))
#endif

PWPairSocket::PWPairSocket()
{
}

PWPairSocket::~PWPairSocket()
{
}

bool
PWPairSocket::initialize(const char* name, int flags, int omode)
{
#ifndef WIN32
	int fd[2];
	if ( 0 != socketpair(AF_LOCAL, SOCK_STREAM, 0, fd) ) return false;

	if ( !attach2(fd) )
	{
		::close(fd[0]);
		::close(fd[1]);
		return false;
	}

	return true;
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

void
PWPairSocket::destroy(void)
{
	int fd[2];
	detach2(fd);

#ifndef WIN32
	if ( -1 != fd[0] ) ::close(fd[0]);
	if ( -1 != fd[1] ) ::close(fd[1]);
#else
	if ( -1 != fd[0] ) ::closesocket(fd[0]);
	if ( -1 != fd[1] ) ::closesocket(fd[1]);
#endif//WIN32
}

ssize_t
PWPairSocket::receive(void* buf, size_t blen, int flag, size_t idx)
{
	return ::recv((idx == 0 ? m_fd : m_fd2 ), __PWX(buf), blen, flag);
}

ssize_t
PWPairSocket::send(const void* buf, size_t blen, int flag, size_t idx)
{
	return ::send((idx == 0 ? m_fd : m_fd2 ), __PWXC(buf), blen, flag);
}

ssize_t
PWPairSocket::read(void* buf, size_t blen, size_t idx)
{
	return ::recv((idx == 0 ? m_fd : m_fd2 ), __PWX(buf), blen, 0);
}

ssize_t
PWPairSocket::write(const void* buf, size_t blen, size_t idx)
{
	return ::send((idx == 0 ? m_fd : m_fd2 ), __PWXC(buf), blen, 0);
}

bool
PWPairSocket::getOption(void* v, size_t* vlen, int lv, int optname,
	size_t idx) const
{
	socklen_t olen;

	if ( 0 == getsockopt(m_fd, lv, optname, __PWX(v), &olen) )
	{
		if ( vlen ) *vlen = (size_t)olen;
		return true;
	}

	return false;
}

bool
PWPairSocket::setOption(const void* v, size_t vlen, int lv,
	int optname, size_t idx)
{
	return 0 == setsockopt(_get_fd(idx), lv, optname, __PWXC(v), (socklen_t)vlen);
}

bool
PWPairSocket::getOptionBoolean(bool& v, int lv,
	int optname, size_t idx) const
{
	int ret;
	socklen_t retsize(sizeof(ret));
	if ( 0 == getsockopt(_get_fd(idx), lv, optname, __PWX(&ret), &retsize) )
	{
		v = (ret != 0);
		return true;
	}

	return false;
}

bool
PWPairSocket::setOptionBoolean(bool v, int lv, int optname, size_t idx)
{
	int ret(v?1:0);
	return 0 == setsockopt(_get_fd(idx), lv, optname, __PWXC(&ret), sizeof(ret));
}

bool
PWPairSocket::getOptionInteger32(int32_t& v, int lv,
	int optname, size_t idx) const
{
	socklen_t retsize(sizeof(v));
	return 0 == getsockopt(_get_fd(idx), lv, optname, __PWX(&v), &retsize);
}

bool
PWPairSocket::setOptionInteger32(int32_t v, int lv, int optname, size_t idx)
{
	return 0 == setsockopt(_get_fd(idx), lv, optname, __PWXC(&v), sizeof(v));
}

bool
PWPairSocket::getOptionInteger64(int64_t& v, int lv,
	int optname, size_t idx) const
{
	socklen_t retsize(sizeof(v));
	return 0 == getsockopt(_get_fd(idx), lv, optname, __PWX(&v), &retsize);
}

bool
PWPairSocket::setOptionInteger64(int64_t v, int lv, int optname, size_t idx)
{
	return 0 == setsockopt(_get_fd(idx), lv, optname, __PWXC(&v), sizeof(v));
}

bool
PWPairSocket::getOptionReuseAddress(size_t idx) const
{
	bool res;
	if ( getOptionBoolean(res, SOL_SOCKET, SO_REUSEADDR, idx) )
	{
		return res;
	}

	return false;
}

bool
PWPairSocket::setOptionReuseAddress(bool v, size_t idx)
{
	return setOptionBoolean(v, SOL_SOCKET, SO_REUSEADDR, idx);
}

bool
PWPairSocket::getOptionKeepAlive(size_t idx) const
{
	bool res;
	if ( getOptionBoolean(res, SOL_SOCKET, SO_KEEPALIVE, idx) )
	{
		return res;
	}

	return false;
}

bool
PWPairSocket::setOptionKeepAlive(bool v, size_t idx)
{
	return setOptionBoolean(v, SOL_SOCKET, SO_KEEPALIVE, idx);
}

size_t
PWPairSocket::getOptionReceiveBuffer(size_t idx) const
{
	int32_t v;
	if ( getOptionInteger32(v, SOL_SOCKET, SO_RCVBUF, idx) )
	{
		return (size_t)v;
	}

	return false;
}

bool
PWPairSocket::setOptionReceiveBuffer(size_t v, size_t idx)
{
	return setOptionInteger32((int32_t)v, SOL_SOCKET, SO_RCVBUF, idx);
}

size_t
PWPairSocket::getOptionSendBuffer(size_t idx) const
{
	int32_t v;
	if ( getOptionInteger32(v, SOL_SOCKET, SO_SNDBUF, idx) )
	{
		return (size_t)v;
	}

	return (size_t)0;
}

bool
PWPairSocket::setOptionSendBuffer(size_t v, size_t idx)
{
	return setOptionInteger32((int32_t)v, SOL_SOCKET, SO_SNDBUF, idx);
}

bool
PWPairSocket::getOptionDelay(size_t idx) const
{
	bool res;
	if ( getOptionBoolean(res, IPPROTO_TCP, TCP_NODELAY, idx) )
	{
		return !res;
	}

	return true;
}

bool
PWPairSocket::setOptionDelay(bool v, size_t idx)
{
	return setOptionBoolean(!v, IPPROTO_TCP, TCP_NODELAY, idx);
}

};//namespace purewell

